package store.lunangangster.engine.impl.entity.mgr;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import store.lunangangster.engine.impl.Context;
import store.lunangangster.engine.impl.entity.ByteArrayEntity;
import store.lunangangster.engine.impl.entity.EntityImpl;
import store.lunangangster.engine.impl.entity.ExecutionEntity;
import store.lunangangster.engine.impl.entity.VariableEntity;
import store.lunangangster.engine.impl.entity.VariableType;

public abstract class VariableScopeImpl extends EntityImpl implements VariableScope {

  private Map<String, VariableEntity> variableEntityMap;

  public ExecutionEntity getExecution() {
    return null;
  }

  @Override
  public Map<String, VariableEntity> getVariableEntities() {
    return collectVariableEntities();
  }

  @Override
  public VariableEntity getVariableEntity(String variableName) {
    ensureVariableEntityMapInitialized();
    // 1. variableEntityMap中可能有父VariableScope的数据，也可能没有
    if (variableEntityMap != null && variableEntityMap.containsKey(variableName)) {
      return variableEntityMap.get(variableName);
    }
    // 2. 查找父Scope
    VariableScope parentScope = getParentVariableScope();
    if (parentScope != null) {
      return parentScope.getVariableEntity(variableName);
    }
    return null;
  }

  @Override
  public Object getVariable(String variableName) {
    VariableEntity variableEntity = getVariableEntity(variableName);
    if (variableEntity != null) {
      return variableEntity.getValue();
    }
    return null;
  }

  @Override
  public Map<String, Object> getVariables() {
    Map<String, VariableEntity> variableEntityMap = collectVariableEntities();
    Map<String, Object> map = new HashMap<>(variableEntityMap.size());

    for (String variableName : variableEntityMap.keySet()) {
      map.put(variableName, variableEntityMap.get(variableName).getValue());
    }
    return map;
  }

  /**
   * 获取VariableScope中的Variables
   * @return 当前变量域中的所有变量
   */
  public abstract List<VariableEntity> loadVariableEntities();

  /**
   * 获取父VariableScope中的Variables
   * @return 父类变量域中的所有变量
   */
  public abstract VariableScope getParentVariableScope();

  /**
   * 设置VariableEntity中的Execution、ProcessInstance的指向
   */
  public abstract void setVariableEntityPointer(VariableEntity variableEntity);

  /**
   *
   * 获取父类、当前对象、以及用户刚设置的尚未入库的Variables，优先级依次递增进行覆盖。
   * 即获取当前对象可获取到的所有域中的Variable
   * @return  variableEntityMap
   */
  protected Map<String, VariableEntity> collectVariableEntities() {
    ensureVariableEntityMapInitialized();

    Map<String, VariableEntity> variables = new HashMap<>();

    // 1. 获取父Variables
    VariableScope parentVariableScope = getParentVariableScope();
    if (parentVariableScope != null) {
      variables.putAll(parentVariableScope.getVariableEntities());
    }

    // 2. 获取数据库中Variables
    variables.putAll(variableEntityMap);

    return variables;
  }

  /**
   * 查询数据库，获取当前对象存储的Variables。仅可执行一次
   */
  protected void ensureVariableEntityMapInitialized() {
    if (variableEntityMap == null) {
      variableEntityMap = new HashMap<>();
      for (VariableEntity variableEntity : loadVariableEntities()) {
        variableEntityMap.put(variableEntity.getName(), variableEntity);
      }
    }
  }

  @Override
  public void setVariable(String variableName, Object value) {
    // 1. 查找当前对象的LocalVariable
    ensureVariableEntityMapInitialized();
    if (variableEntityMap.containsKey(variableName)) {
      variableEntityMap.get(variableName).setValue(value);
      return;
    }
    // 2. 如果还查找不到，则向父类Variable中继续查找
    VariableScope parentVariableScope = getParentVariableScope();
    if (parentVariableScope != null) {
      parentVariableScope.setVariable(variableName, value);
      return;
    }
    // 3. 如果到达最上层父类，则在最上层VariableScope中创建Variable。保证Variable的可见性
    createVariableLocal(variableName, value);
  }

  @Override
  public void setVariables(Map<String, Object> variables) {
    variables.forEach(this::setVariable);
  }

  @Override
  public void setLocalVariable(String variableName, Object value) {
    // 1. 查找当前对象的LocalVariable
    ensureVariableEntityMapInitialized();
    if (variableEntityMap.containsKey(variableName)) {
      variableEntityMap.get(variableName).setValue(value);
      return;
    }
    // 2. 如果查找不到，则在当前VariableScore中进行创建
    createVariableLocal(variableName, value);
  }

  @Override
  public void setLocalVariables(Map<String, Object> variables) {
    variables.forEach(this::setLocalVariable);
  }

  /**
   * 在当前的VariableScope中创建Variable
   * @param variableName  变量名
   * @param value         变量值
   */
  protected void createVariableLocal(String variableName, Object value) {
    VariableEntityManager variableEntityManager = Context.getProcessEngineConfiguration()
        .getVariableEntityManager();

    VariableEntity variableEntity = variableEntityManager.create();

    variableEntity.setName(variableName);
    variableEntity.setValue(value);
    setVariableEntityPointer(variableEntity);

    // insert ByteArray to get id
    if (variableEntity.getType().equals(VariableType.BYTES_TYPE)) {
      createByteArrayOnVariable(variableEntity);
    }

    variableEntityManager.insert(variableEntity);
    variableEntityMap.put(variableEntity.getName(), variableEntity);
  }

  private void createByteArrayOnVariable(VariableEntity variableEntity) {
    ByteArrayManager byteArrayManager = Context.getProcessEngineConfiguration()
        .getByteArrayManager();
    ByteArrayEntity byteArrayEntity = byteArrayManager.create();
    byteArrayEntity.setName(variableEntity.getName());
    byteArrayEntity.setBytes(variableEntity.getBytesValue());
    byteArrayManager.insert(byteArrayEntity);

    variableEntity.setByteArrayId(byteArrayEntity.getId());
  }
}
